﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace CodePlex.Resourceful.Microsoft.SSDS
{
    public class SSDSClient : Client
    {


        private readonly string _username;
        private readonly string _password;
        private readonly bool _useSSL;

        public SSDSClient(string username, string password, bool useSSL) : base((Uri)null) 
        {
            _username = username;
            _password = password;
            _useSSL = useSSL;

            string auth = Utils.ToBase64(Encoding.UTF8.GetBytes(string.Concat(_username, ':', _password)));
            this.CommonHeaders[HttpHeader.Authorization] = "Basic " + auth;
        
        }


        // authority 

        public ClientResponse CreateAuthority(string authority)
        {
            this.ResetRootUri(null);

            string body = string.Format(
                  @"<s:Authority xmlns:s='{0}'>
                        <s:Id>{1}</s:Id>
                      </s:Authority>", SSDSConstants.Namespaces.Sitka, authority);


            return this.CommonPost("", body);
            //401 Unauthorized
            //Content-Length:0
            //Date:Sat, 31 May 2008 18:38:50 GMT
            //WWW-Authenticate:Basic realm=""

            //201 Created
            //x-msft-request-id:9c61067a-0077-47b5-bf83-e7ec64e11f16
            //$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/
            //Content-Length:0
            //Content-Type:application/xml; charset=utf-8
            //Date:Sat, 31 May 2008 18:15:00 GMT

            //409 Conflict
            //x-msft-request-id:c53aec44-8e43-49df-b005-171e5490e8bd
            //Content-Length:168
            //Content-Type:application/xml; charset=utf-8
            //Date:Sat, 31 May 2008 18:35:17 GMT
            //<s:Error xmlns:s="http://schemas.microsoft.com/sitka/2008/03/">
            //  <s:Code>1002</s:Code>
            //  <s:Message>The specified entity 'auth1' already exists.</s:Message>
            //</s:Error>

        }

        public ClientResponse GetAuthority(string authority)
        {
            // https://<authority-id>.data.beta.mssds.com/v1/
            this.ResetRootUri(authority);

            return this.GetRepresentation("");

            //200 OK
            //x-msft-request-id:43c11832-d3fc-46e6-a9c4-b4a36173fccf
            //$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/
            //Content-Length:227
            //Content-Type:application/xml; charset=utf-8
            //Date:Sat, 31 May 2008 18:57:47 GMT
            //<s:Authority xmlns:s="http://schemas.microsoft.com/sitka/2008/03/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
            //  <s:Id>auth1</s:Id>
            //  <s:Version>6</s:Version>
            //</s:Authority>

        }


















        public ClientResponse QueryContainers(string authority, string whereClause)
        {

            string q = this.BuildQuery(whereClause);

            // http://<authority-id>.data.beta.mssds.com/v1/?q='' 
            this.ResetRootUri(authority);

            return this.GetRepresentation("", NameValuePairs.From("q", q));

            //200 OK
            //x-msft-request-id:8422c288-22ea-494f-b07b-09db6ed0bdf5
            //$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/?q=''
            //Content-Length:264
            //Content-Type:application/xml; charset=utf-8
            //Date:Sat, 31 May 2008 18:55:24 GMT
            //<s:EntitySet xmlns:s="http://schemas.microsoft.com/sitka/2008/03/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
            //  <s:Container>
            //    <s:Id>first</s:Id>
            //    <s:Version>1</s:Version>
            //  </s:Container>
            //</s:EntitySet>


        }


     

        public ClientResponse QueryEntities(string authority, string containerID, string whereClause)
        {


            string q = this.BuildQuery(whereClause);

            // https://<authority-id>.data.beta.mssds.com/v1/
            this.ResetRootUri(authority);

            return this.GetRepresentation(containerID,NameValuePairs.From("q", q));


//200 OK
//x-msft-request-id:5a5cdfd9-c135-42a6-aa8d-8c22f91a819c
//$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/first?q=from+e+in+entities+select+e
//Content-Length:165
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 19:45:14 GMT
//<s:EntitySet xmlns:s="http://schemas.microsoft.com/sitka/2008/03/" xmlns:xsi="htp://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchma">
//  <Bob>
//    <s:Id>asdf</s:Id>
//    <s:Version>1</s:Version>
//  </Bob>
//</s:EntitySet>

        }



        private string BuildQuery(string whereClause)
        {
            whereClause = whereClause ?? "";
            whereClause = whereClause.Trim();

            string q = "";
            if (whereClause.Length > 0)
            {
                q = "'from e in entities where " + whereClause + " select e'";
            }
            // >, >=, <, <=, ==, != 
            // && (and), || (or),! (not)

            return q;
        }







        public ClientResponse DeleteContainer(string authority, string containerID)
        {
            this.ResetRootUri(authority);

            return this.DeleteRepresentation(containerID);

//200 OK
//x-msft-request-id:4b347972-3ab9-4871-bb47-e37faf94cd8d
//$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/first
//Content-Length:0
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 19:00:32 GMT


//404 Not Found
//x-msft-request-id:ab7b9079-0fb3-4012-85a6-52c0f8c92b3f
//$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/first
//Content-Length:155
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 19:00:57 GMT
//<s:Error xmlns:s="http://schemas.microsoft.com/sitka/2008/03/">
//  <s:Code>1001</s:Code>
//  <s:Message>Entity 'first' cannot be found.</s:Message>
//</s:Error>
        }


        public ClientResponse DeleteEntity(string authority, string containerID, string entityID)
        {
            // https://<authority-id>.data.beta.mssds.com/v1/<container-id>/<entity-id>
            this.ResetRootUri(authority);

            return this.DeleteRepresentation(containerID + "/" + entityID);

//200 OK
//x-msft-request-id:29eea22f-7470-4926-bf37-c17999081684
//$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/first/asdf
//Content-Length:0
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 19:36:03 GMT

        }

        public ClientResponse GetEntity(string authority, string containerID, string entityID)
        {
            return this.GetEntity(authority, containerID, entityID, null);
        }
        public ClientResponse GetEntity(string authority, string containerID, string entityID, bool bufferOutput)
        {
            return this.GetEntity(authority, containerID, entityID, null, bufferOutput);
        }
        public ClientResponse GetEntity(string authority, string containerID, string entityID, NameValuePairs headers)
        {
            return this.GetEntity(authority, containerID, entityID, headers, true);
        }
        public ClientResponse GetEntity(string authority, string containerID, string entityID, NameValuePairs headers, bool bufferOutput)
        {
            // https://<authority-id>.data.beta.mssds.com/v1/<container-id>/<entity-id>
            this.ResetRootUri(authority);

            string path = containerID + "/" + entityID;

            GetRequest request = GetRequest.Create(path, (NameValuePairs)null, headers);
            request.BufferOutput = bufferOutput;
            return this.GetRepresentation(request);

//200 OK
//x-msft-request-id:78c16556-1027-4248-854c-78cfd93fa818
//$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/first/asdf
//Content-Length:210
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 19:34:28 GMT
//<Bob xmlns:s="http://schemas.microsoft.com/sitka/2008/03/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
//  <s:Id>asdf</s:Id>
//  <s:Version>2</s:Version>
//</Bob>

        }

        public ClientResponse UpdateEntity(string authority, string containerID, string entityID, string kind, params FlexibleProperty[] properties)
        {
            // https://<authority-id>.data.beta.mssds.com/v1/<container-id>/<entity-id>
            this.ResetRootUri(authority);

            string entity = MakeEntity(entityID, kind, properties);
            return this.CommonPut(containerID + "/" + entityID, entity);

//200 OK
//x-msft-request-id:a766b414-91b5-447a-9b92-6d2d69bd40bc
//$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/first/asdf
//Content-Length:210
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 19:32:02 GMT
//<Bob xmlns:s="http://schemas.microsoft.com/sitka/2008/03/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
//  <s:Id>asdf</s:Id>
//  <s:Version>0</s:Version>
//</Bob>


        }



        public ClientResponse CreateEntity(string authority, string containerID, string entityID, string kind, params FlexibleProperty[] properties)
        {
            
            // "https://books-docsamples.data.beta.mssds.com/v1/UsedBooks";
            this.ResetRootUri(authority);

            string entity = MakeEntity(entityID, kind, properties);
            return this.CommonPost(containerID, entity);

//201 Created
//x-msft-request-id:e892ff0b-55a4-4552-abf8-10c2cb99b36d
//$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/first
//Content-Length:0
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 19:16:39 GMT

        }

        public ClientResponse CreateEntityBlob(string authority, string containerID, string entityID, string contentType, long contentLength, string contentDisposition, Stream input, Action<long> bytesTransferred)
        {
            // "https://books-docsamples.data.beta.mssds.com/v1/UsedBooks";
            this.ResetRootUri(authority);

            NameValuePairs headers = new NameValuePairs();
            headers[HttpHeader.ContentType] = contentType;
            headers[SSDSConstants.Headers.Slug] = entityID;
            headers[HttpHeader.ContentLength] = contentLength.ToString(); // this is the trigger for Client to do non-buffered input
            if (contentDisposition != null)
            {
                headers[SSDSConstants.Headers.ContentDisposition] = contentDisposition;
            }
            return this.CommonPost(containerID, headers, input, bytesTransferred);

        }

        public ClientResponse UpdateEntityBlob(string authority, string containerID, string entityID, string contentType, long contentLength, string contentDisposition, Stream input, Action<long> bytesTransferred)
        {
            // "https://books-docsamples.data.beta.mssds.com/v1/UsedBooks";
            this.ResetRootUri(authority);

            NameValuePairs headers = new NameValuePairs();
            headers[HttpHeader.ContentType] = contentType;
          //  headers[SSDSConstants.Headers.Slug] = entityID;
            headers[HttpHeader.ContentLength] = contentLength.ToString(); // this is the trigger for Client to do non-buffered input
            if (contentDisposition != null)
            {
                headers[SSDSConstants.Headers.ContentDisposition] = contentDisposition;
            }
            return this.CommonPut(containerID + "/" + entityID, headers, input, bytesTransferred);

        }


        //public ClientResponse GetEntityBlob(string authority, string containerID, string entityID)
        //{
        //    // "https://books-docsamples.data.beta.mssds.com/v1/UsedBooks/asdf";
        //    this.ResetRootUri(authority);

        //    return this.GetRepresentation(containerID + "/" + entityID);

        //}







        public ClientResponse CreateContainer(string authority, string containerID)
        {
            this.ResetRootUri(authority);

            string body = string.Format(
                    @"<s:Container xmlns:s='{0}'>
                        <s:Id>{1}</s:Id>
                      </s:Container>", SSDSConstants.Namespaces.Sitka, containerID);




            return this.CommonPost("", body);

//201 Created
//x-msft-request-id:82eafad7-b077-4870-93db-52b9a2b54c01
//$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/
//Content-Length:0
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 18:46:05 GMT


//409 Conflict
//x-msft-request-id:0be4d291-a798-496e-95e5-b4b805ff2633
//Content-Length:168
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 18:46:57 GMT
//<s:Error xmlns:s="http://schemas.microsoft.com/sitka/2008/03/">
//  <s:Code>1002</s:Code>
//  <s:Message>The specified entity 'first' already exists.</s:Message>
//</s:Error>




        }


     



        public ClientResponse GetContainer(string authority, string containerID)
        {
            //  https://<authority-id>.data.beta.mssds.com/v1/<container-id> 
            this.ResetRootUri(authority);
            
            return this.GetRepresentation(containerID);

//200 OK
//x-msft-request-id:708398ba-77d1-4e27-ba03-310737e86dfe
//$MsftRequestUri:http://auth1.data.beta.mssds.com/v1/first
//Content-Length:227
//Content-Type:application/xml; charset=utf-8
//Date:Sat, 31 May 2008 18:50:25 GMT
//<s:Container xmlns:s="http://schemas.microsoft.com/sitka/2008/03/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
//  <s:Id>first</s:Id>
//  <s:Version>1</s:Version>
//</s:Container>
        }















        private void ResetRootUri(string authority)
        {

            string uri = string.Format("http{0}://{1}data.beta.mssds.com/v1/",
                _useSSL?"s":"",
                authority == null ? "" : (authority + "."));

            this.RootUri = new Uri(uri);

        }

        private string MakeEntity(string entityID, string kind, params FlexibleProperty[] properties)
        {

            //<MyType xmlns:s=http://schemas.microsoft.com/sitka/2008/03/
            //        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            //        xmlns:x="http://www.w3.org/2001/XMLSchema">
            //   <s:Id>sampleEntity</s:Id>
            //   <s:Version>1</s:Version>
            //   <uri xsi:type="x:string">http://foobar/blah</uri>
            //   <num xsi:type="x:decimal">3</num>
            //   <bin xsi:type="x:base64Binary">AQID</bin>
            //   <created xsi:type="x:dateTime">2008-02-09T09:08:55.8937229</created>
            //   <isTrue xsi:type="x:boolean">true</isTrue>
            //</MyType>




            StringBuilder body = new StringBuilder();
            body.AppendFormat(
                @"<{0} xmlns:s='{1}'
                         xmlns:xsi='{2}'
                         xmlns:x='{3}' >
                     <s:Id>{4}</s:Id>
", kind, SSDSConstants.Namespaces.Sitka,Namespaces.XMLSchemaInstance,Namespaces.XMLSchema, entityID);

            foreach (FlexibleProperty p in properties)
            {
                string xsiType = p.Code.ToString();
                xsiType = xsiType.Substring(0, 1).ToLower() + xsiType.Substring(1);

                string value;
                if (p.Code == System.Xml.Schema.XmlTypeCode.Base64Binary)
                {
                    value = Utils.ToBase64((byte[])p.Value);
                }
                else if (p.Code == System.Xml.Schema.XmlTypeCode.DateTime)
                {
                    value = Utils.Iso8601Date((DateTime)p.Value);
                }
                else
                {
                    value = Convert.ToString(p.Value);
                }


                body.AppendFormat("<{0} xsi:type='x:{2}'>{1}</{0}>\r\n", p.Name, value, xsiType);
            }
            body.AppendFormat("</{0}>", kind);

            return body.ToString();
        }


        private ClientResponse CommonPost(string path, string body)
        {
            return this.CommonPost(path, NameValuePairs.From(HttpHeader.ContentType, MediaType.ApplicationExSsdsXml), Utils.ToStream(body),null);
        }
        private ClientResponse CommonPost(string path, NameValuePairs headers, Stream body, Action<long> bytesTransferred)
        {
            PostRequest request = PostRequest.Create(path, headers, body);
            request.BytesIn = bytesTransferred;
            return this.PostRepresentation(request);
        }

        private ClientResponse CommonPut(string path, string body)
        {
            return this.CommonPut(path, NameValuePairs.From(HttpHeader.ContentType, MediaType.ApplicationExSsdsXml), Utils.ToStream(body),null);
        }
        private ClientResponse CommonPut(string path, NameValuePairs headers, Stream body, Action<long> bytesTransferred)
        {
            PutRequest request = PutRequest.Create(path, headers, body);
            request.BytesIn = bytesTransferred;
            return this.PutRepresentation(request);
        }






    }
}
